Skip to content

Drop sorbet-runtime dependency by migrating to RBS comment annotations#68

Merged
dduugg merged 6 commits into
mainfrom
remove-sorbet-runtime-dependency
May 29, 2026
Merged

Drop sorbet-runtime dependency by migrating to RBS comment annotations#68
dduugg merged 6 commits into
mainfrom
remove-sorbet-runtime-dependency

Conversation

@dduugg
Copy link
Copy Markdown
Contributor

@dduugg dduugg commented May 28, 2026

Summary

Removes this gem's runtime dependency on sorbet-runtime by migrating every type annotation from Sorbet's runtime DSL to inline RBS comment syntax, following upstream packwerk (Shopify/packwerk#455). This supersedes #67 (which closed the gap by adding more require 'sorbet-runtime' statements) — instead we eliminate runtime Sorbet usage entirely.

Static type checking is unchanged in spirit: srb tc still runs, now reading the #: RBS comments via --parser=prism --enable-experimental-rbs-comments.

Changes

RBS migration (drop sorbet-runtime)

  • sorbet/config: add --parser=prism and --enable-experimental-rbs-comments.
  • Translate all sig { … } blocks to #: RBS comments (via spoom srb sigs translate); @override annotations preserved.
  • Convert the four T::Struct Package classes to plain classes with RBS-annotated attr_readers and keyword initialize methods.
  • Replace T.let / T.must / extend T::Sig with inline RBS annotations.
  • Drop require 'sorbet-runtime' and remove the sorbet-runtime gem dependency.

Sorbet tooling + redundant T.let cleanup

  • Bump sorbet, tapioca, spoom, rbi, rbs in the lockfile (and regenerate gem RBIs).
  • Apply Sorbet/RedundantTLetForLiteral and Sorbet/RedundantTLet, stripping redundant T.let wrappers from literal constants.
  • Pin rubocop-sorbet to Shopify/rubocop-sorbet@7b7d3cb (after #367 and #372) so those cops are available — neither is in the released v0.12.0 yet.

RuboCop config

  • Allow RBS inline annotation comments (Layout/LeadingCommentSpace: AllowRBSInlineAnnotation).
  • Set TargetRubyVersion: 3.3.
  • Remove disabled-cop entries that no longer have any violations (incl. Style/TrivialAccessors, whose Sorbet-sig rationale is moot now that RBS makes annotated attr_readers clean).

Minimum Ruby version

  • Raise required_ruby_version to >= 3.3. The modernized toolchain pulls in rbi >= 0.3.12, which requires Ruby 3.3+. (Ruby 3.2 is also EOL; it was dropped from CI in rubyatscale/shared-config#19, now merged.)

Verification

  • bundle exec srb tc → No errors
  • bundle exec rake test → 88 runs, 0 failures
  • bundle exec rubocop → no offenses
  • CI green across Ruby 3.3, 3.4, and 4.

Follow-up

  • Once a rubocop-sorbet release ships with the two T.let cops, the git pin in the Gemfile can move back to a rubygems version.

dduugg added 5 commits May 28, 2026 15:31
Bump sorbet, sorbet-runtime, tapioca, spoom, rbi, and rbs to current
versions and regenerate the gem RBIs to match.

Pin rubocop-sorbet to Shopify/rubocop-sorbet@7b7d3cb (post-#367 and
post-#372) so the not-yet-released Sorbet/RedundantTLetForLiteral and
Sorbet/RedundantTLet cops are available.
Apply Sorbet/RedundantTLetForLiteral and Sorbet/RedundantTLet
autocorrections. Sorbet infers the type of literal constants
automatically, so the explicit T.let wrappers were redundant.
Replace Sorbet's runtime DSL with inline RBS comment annotations so the
gem no longer depends on sorbet-runtime at runtime, matching upstream
packwerk (Shopify/packwerk#455).

- Enable --parser=prism and --enable-experimental-rbs-comments in
  sorbet/config
- Translate all sig blocks to #: RBS comments (via spoom)
- Convert the T::Struct Package classes to plain classes with
  RBS-annotated attr_readers and keyword initializers
- Replace T.let / T.must / extend T::Sig with inline RBS annotations
- Drop require 'sorbet-runtime' and the sorbet-runtime gem dependency
- Allow RBS inline annotation comments (Layout/LeadingCommentSpace)
- Set TargetRubyVersion to 3.2 (matches required_ruby_version), fixing
  Lint/RedundantRequireStatement on the binstubs
- Remove disabled-cop entries that no longer have any violations,
  including Style/TrivialAccessors (its Sorbet-sig rationale is moot now
  that RBS makes annotated attr_readers clean)
The modernized Sorbet toolchain pulls in rbi 0.3.12, which requires
Ruby >= 3.3. Raise required_ruby_version and TargetRubyVersion to match.
@dduugg dduugg changed the title Drop sorbet-runtime dependency via RBS comment annotations Drop sorbet-runtime dependency by migrating to RBS comment annotations May 28, 2026
@dduugg dduugg marked this pull request as ready for review May 28, 2026 23:02
@dduugg dduugg requested a review from a team as a code owner May 28, 2026 23:02
@dduugg dduugg merged commit f081cca into main May 29, 2026
10 of 13 checks passed
@dduugg dduugg deleted the remove-sorbet-runtime-dependency branch May 29, 2026 17:56
@github-project-automation github-project-automation Bot moved this from Triage to Done in Modularity May 29, 2026
dduugg added a commit that referenced this pull request May 29, 2026
…total methods (#71)

* Migrate tests off sorbet-runtime and trim gemspec sorbet deps

The migration in #68 left the test suite using Sorbet's runtime DSL
(extend T::Sig, sig, T.must, T.unsafe, T.bind), which only worked
because packwerk transitively loaded sorbet-runtime. packwerk 3.3.0
drops sorbet-runtime, so the suite fails with 'uninitialized constant T'.

Rather than re-add a sorbet-runtime dependency, finish the migration in
test/ the same way as lib/:

- Translate sig blocks to #: RBS comments and drop extend T::Sig
- Replace requires_ancestor/T.bind with a Minitest::Runnable
  @requires_ancestor annotation in the fixture helper
- Convert T.must to '#: as !nil' and T.unsafe to '#: as untyped' casts

Also drop the redundant 'sorbet-static' dev dependency (the 'sorbet'
gem already depends on it).

* Avoid untyped cast in integration specs

Use Enumerable#any? with the class as a === pattern instead of casting
the abstract Packwerk::Checker element to untyped to call is_a?. This
keeps the check fully typed (no type erasure).

* Use Array#fetch(-1) instead of last with a non-nil cast

fetch(-1) returns a non-nil element type, so no '#: as !nil' cast is
needed (and it raises IndexError rather than silently returning nil).

* Use total methods instead of non-nil casts in privacy checker

- publicized_locations.fetch(location) instead of [] with '#: as !nil'
  (the key is guaranteed present by the preceding key? guard)
- lines.first(5) instead of lines[0..4] with '#: as !nil' (first(n)
  returns a non-nil Array)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant